package com.basic.backend.service.impl;

import com.basic.backend.config.RabbitMQConfig;
import com.basic.backend.model.dto.analysis.AnalysisRequest;
import com.basic.backend.model.dto.analysis.AnalysisResponse;
import com.basic.backend.model.dto.analysis.ArticleAnalysisDTO;
import com.basic.backend.service.impl.AnalysisResultService;
import com.rabbitmq.client.Channel;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.support.AmqpHeaders;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.*;
import org.springframework.messaging.handler.annotation.Header;
import org.springframework.stereotype.Service;
import org.springframework.web.client.HttpServerErrorException;
import org.springframework.web.client.ResourceAccessException;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;
import java.io.IOException;
import java.time.Duration;
import java.time.LocalDate;
import java.util.Optional;
import java.util.concurrent.TimeUnit;

@Slf4j
@Service
public class ArticleAnalysisConsumer {

    @Resource
    private RestTemplate restTemplate;
    @Resource
    private AnalysisResultService resultService;
    @Resource
    private RedisTemplate<String, Integer> redisTemplate;

    @Value("${analysis.service.url}")
    private String analysisServiceUrl;
    @Value("${retry.max-attempts:3}")
    private int maxRetries;
    @Value("${retry.initial-interval:5000}")
    private long initialInterval;
    @Value("${retry.multiplier:2}")
    private double multiplier;

    @RabbitListener(queues = RabbitMQConfig.ARTICLE_QUEUE)
    public void processAnalysisTask(
            ArticleAnalysisDTO dto,
            Message message,
            @Header(AmqpHeaders.DELIVERY_TAG) long deliveryTag,
            Channel channel
    ) {
        final Long articleId = dto.getArticleId();
        String redisKey = "retry:article:" + articleId;

        try {
            log.info("开始处理文章分析任务 articleId={}", articleId);

            // 获取当前重试次数
            int retryCount = Optional.ofNullable(redisTemplate.opsForValue().get(redisKey))
                    .orElse(0);

            if (retryCount >= maxRetries) {
                handleFinalFailure(articleId, deliveryTag, channel, redisKey);
                return;
            }

            // 构建请求
            HttpEntity<AnalysisRequest> entity = new HttpEntity<>(
                    buildAnalysisRequest(dto),
                    createHeaders()
            );

            // 带退避策略的请求执行
            ResponseEntity<AnalysisResponse> response = executeWithRetry(entity, retryCount);

            // 处理成功结果
            resultService.saveResult(articleId, response.getBody());
            log.info("文章分析成功 articleId={}", articleId);
            channel.basicAck(deliveryTag, false);
            redisTemplate.delete(redisKey);

        } catch (Exception e) {
            handleRetry(articleId, deliveryTag, channel, redisKey, e);
        }
    }

    private ResponseEntity<AnalysisResponse> executeWithRetry(HttpEntity<AnalysisRequest> entity, int retryCount)
            throws InterruptedException {
        try {
            // 计算退避时间
            long backoffTime = (long) (initialInterval * Math.pow(multiplier, retryCount));
            Thread.sleep(backoffTime);

            return restTemplate.exchange(
                    analysisServiceUrl,
                    HttpMethod.POST,
                    entity,
                    AnalysisResponse.class
            );
        } catch (ResourceAccessException e) {
            throw new AnalysisException("NETWORK_ERROR", "网络连接异常", e);
        } catch (HttpServerErrorException e) {
            throw new AnalysisException("UPSTREAM_ERROR", "上游服务异常: " + e.getStatusCode(), e);
        }
    }

    private void handleRetry(Long articleId, long deliveryTag, Channel channel,
                             String redisKey, Exception e) {
        try {
            Long newRetryCount = redisTemplate.opsForValue().increment(redisKey, 1);
            redisTemplate.expire(redisKey, Duration.ofHours(24));

            if (newRetryCount >= maxRetries) {
                handleFinalFailure(articleId, deliveryTag, channel, redisKey);
            } else {
                log.warn("分析失败等待重试 articleId={}, 次数={}, 原因: {}",
                        articleId, newRetryCount, e.getMessage());
                channel.basicNack(deliveryTag, false, true);
            }
        } catch (IOException ex) {
            log.error("消息应答失败 articleId={}", articleId, ex);
        }
    }

    private void handleFinalFailure(Long articleId, long deliveryTag,
                                    Channel channel, String redisKey) throws IOException {
        log.error("达到最大重试次数 articleId={}", articleId);
        channel.basicReject(deliveryTag, false);
        redisTemplate.delete(redisKey);
        // 发送到死信队列或记录审计日志（需配置DLX）
    }

    private HttpHeaders createHeaders() {
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        headers.set("X-Request-Source", "article-consumer");
        return headers;
    }

    private AnalysisRequest buildAnalysisRequest(ArticleAnalysisDTO dto) {
        return new AnalysisRequest()
                .setTitle(dto.getTitle())
                .setContent(dto.getContent())
                .setMaxLength(300)
                .setPublishTime(LocalDate.now().toString());
    }

    // 自定义异常类
    private static class AnalysisException extends RuntimeException {
        private final String errorType;

        public AnalysisException(String errorType, String message, Throwable cause) {
            super(message, cause);
            this.errorType = errorType;
        }
    }
}